#include "dma.h"
#if USE_DMA
#include <stdio.h>
#include "../../board.h"

#define DMA_IRQ                          47
#define DMA_CHANNEL_NUM                  32
#define DMA_BASE_ADDR_FOR_ATCM           0xB0010000
#define ATCM_BASE                        0x0
#define ATCM_SIZE                        (64 * 1024)
#define RESET_DMA_CHANNEL                (DCSR_NODESC)
void
dmac_map_device_to_channel(int device_num, int channel)
{
  unsigned offset;

  if(device_num < 64) {
    offset = DMA_RCMR0_63 + 4 * device_num;
  } else {
    offset = DMA_RCMR64_99 + 4 * (device_num - 64);
  }

  writel(DRCMR_MAPVLD | channel, DMA_BASE + offset);
}
int
dma_xfer(struct dma_op_t *op)
{
  unsigned dma_width, cmd;

  switch(op->width) {
  case 1:
    dma_width = DCMD_WIDTH1;
    break;
  case 2:
    dma_width = DCMD_WIDTH2;
    break;
  default:
    dma_width = DCMD_WIDTH4;
    break;
  }

  if(op->len > 0x1fff) {
    printf("Err: dma_xfer len is too large > 0x1fff!!! \n");
    return -1;
  }

  cmd = (unsigned)((op->len) | (dma_width) |
                   ((op->burst_size) << DCMD_MAX_BURST_SIZE_SHIFT) |
                   ((op->flow_dest) << DCMD_FLOWTRG_SHIFT) |
                   ((op->flow_src) << DCMD_FLOWSRC_SHIFT) |
                   ((op->inc_dest_addr) << DCMD_INCTRGADDR_SHIFT) |
                   ((op->inc_src_addr) << DCMD_INCSRCADDR_SHIFT) |
                   DCMD_ENDIRQEN);

  writel(RESET_DMA_CHANNEL, DCSR(op->channel));
  writel(op->src, DSADR(op->channel));
  writel(op->dest, DTADR(op->channel));
  writel(cmd, DCMD(op->channel));
  writel(RESET_DMA_CHANNEL | DCSR_RUN, DCSR(op->channel));

  return 0;
}
int
dma_channel_running(int channel)
{
  unsigned val;

  val = readl(DCSR(channel));

  return (val & DCSR_RUN) ? 1 : 0;
}
unsigned
dma_mapped_addr(unsigned addr)
{
  unsigned atcm_start, atcm_end, addr_dma;

  atcm_start = ATCM_BASE;
  atcm_end = atcm_start + ATCM_SIZE;

  if((addr >= atcm_start) && (addr < atcm_end)) {
    addr_dma = addr + DMA_BASE_ADDR_FOR_ATCM;
  } else {
    addr_dma = addr;
  }

  return addr_dma;
}
void
dma_register_ch_cb(int ch, dma_ch_complete_t cb, void *arg)
{
  if(ch >= DMA_CHANNEL_NUM) {
    printf("dma_register_ch_cb, invalid ch %d\n", ch);
    return;
  }
  dma_irq_callback_register((dma_callback_t)cb, ch);
}
#endif
